using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Xml;

namespace ExpressesClass.BestApi.Utils
{
    public class XmlUtils
    {
        public static string objToXml(object obj)
        {
            string str = obj.ToString();
            string nodeName = str.Substring(str.LastIndexOf(".") + 1);
             if (nodeName.EndsWith("Rsp"))
            {
                nodeName = "response";
            } else {
                nodeName = "request";
            }

            StringBuilder sb = new StringBuilder();
            sb.Append("<").Append(nodeName).Append(">");
            sb.Append(MakeXml(obj));
            sb.Append("</").Append(nodeName).Append(">");
            return sb.ToString();
        }

        static string MakeXml(object obj)
        {
            if (obj == null)
            {
                return null;
            }
            PropertyInfo[] properties = obj.GetType().GetProperties();
            string responseXml = String.Empty;
            StringBuilder sb = new StringBuilder();
            foreach (PropertyInfo item in properties)
            {
                string propertyName = item.Name;
                object propertyValue = item.GetValue(obj);
                if (propertyValue != null)
                {
                    if (item.PropertyType == typeof(Boolean))
                    {
                        responseXml = BaseConvert(obj, propertyName, propertyValue.ToString().ToLower());
                        sb.Append(responseXml);
                        continue;
                    }
                    string propertyValueString = propertyValue.ToString();
                    if (propertyValueString.Contains("&") || propertyValueString.Contains("<") || propertyValueString.Contains(">") || propertyValueString.Contains("'") || propertyValueString.Contains("\""))
                    {
                        if (!propertyValueString.Contains("<![CDATA[")) {
                        	propertyValue = "<![CDATA[" + propertyValue + "]]>";
                        }
                    }
                    if (item.PropertyType == typeof(string) || item.PropertyType.IsPrimitive || item.PropertyType == typeof(int?) || item.PropertyType == typeof(double?) || item.PropertyType == typeof(float?) || item.PropertyType == typeof(long?))//�������ͺ�string�������򵥵�ƴ��
                    {
                        responseXml = BaseConvert(obj, propertyName, propertyValue);
                        sb.Append(responseXml);
                    }
                    else if (item.PropertyType.IsGenericType && item.PropertyType.GetGenericTypeDefinition() == typeof(List<>))//LIST���������´���
                    {
                        string itemName = item.Name;
                        object itemValue = item.GetValue(obj);
                        responseXml = ListConvert(itemName, itemValue);
                        sb.Append(responseXml);
                    }
                    else //�������ͽ��еݹ�
                    {
                        responseXml = ObjectConvert(obj, propertyName, propertyValue);
                        sb.Append(responseXml);
                    }
                }
            }
            return sb.ToString();
        }

        static string BaseConvert(object obj, string propertyName, object propertyValue)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<").Append(propertyName)
                    .Append(">").Append(propertyValue)
                    .Append("</").Append(propertyName)
                    .Append(">").Append("\n");
            return sb.ToString();
        }

        static string ListConvert(string itemName, object itemValue)
        {
            StringBuilder sb = new StringBuilder();
            Type genericType = itemValue.GetType().GetGenericArguments()[0];
            int count = Convert.ToInt32(itemValue.GetType().GetProperty("Count").GetValue(itemValue, null));
            if (genericType.IsValueType || genericType == typeof(string))
            {
                for (int i = 0; i < count; i++)
                {
                    object item = itemValue.GetType().GetProperty("Item").GetValue(itemValue, new object[] { i });
                    sb.Append("<" + itemName + ">");
                    sb.Append(item);
                    sb.Append("</" + itemName + ">");
                }
            }
            else
            {
                for (int i = 0; i < count; i++)
                {
                    object item = itemValue.GetType().GetProperty("Item").GetValue(itemValue, new object[] { i });
                    sb.Append("<" + itemName + ">" + MakeXml(item) + "</" + itemName + ">");
                }
            }
            return sb.ToString();
        }

        static string ObjectConvert(object obj, string propertyName, object propertyValue)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<").Append(propertyName)
                    .Append(">").Append("\n")
                    .Append(MakeXml(propertyValue))
                    .Append("</").Append(propertyName)
                    .Append(">").Append("\n");
            return sb.ToString();
        }

        public static object xmlToObj(string xml, Type type)
        {
            object result = null;
            if (xml != null)
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(xml);
                PropertyInfo[] properties = type.GetProperties();
                XmlElement xe = doc.DocumentElement;
                result = convert(xe, type);
            }
            return result;
        }

        static object convertList(List<XmlNode> xmlNodes, Type type)
        {
            object result = type.Assembly.CreateInstance(type.FullName);
            Type genericType = type.GetGenericArguments()[0];
            MethodInfo methodinfo = type.GetMethod("Add");
            foreach (XmlNode xn in xmlNodes)
            {
                if (genericType == typeof(string))
                {
                    methodinfo.Invoke(result, new object[] { xn == null ? null : xn.InnerText });
                }
                else {
                    methodinfo.Invoke(result, new object[] { convert(xn, genericType) });
                }

            }
            return result;
        }

        static object convert(XmlNode node, Type type)
        {
            PropertyInfo[] properties = type.GetProperties();
            object result = type.Assembly.CreateInstance(type.FullName);

            foreach (PropertyInfo info in properties)
            {
                string propertyName = info.Name;
                string value = MakeValue(node, propertyName);
                if (value != null)
                {
                    Type propType = info.PropertyType;
                    if (propType == typeof(string))
                    {
                        info.SetValue(result, value, null);
                    }
                    else if (propType == typeof(int?))
                    {
                        int convert = Convert.ToInt32(value);
                        info.SetValue(result, convert, null);
                    }
                    else if (propType == typeof(float?))
                    {
                        float convert = Convert.ToSingle(value);
                        info.SetValue(result, convert, null);
                    }
                    else if (propType == typeof(long?))
                    {
                        long convert = Convert.ToInt64(value);
                        info.SetValue(result, convert, null);
                    }
                    else if (propType == typeof(double?))
                    {
                        double convert = Convert.ToDouble(value);
                        info.SetValue(result, convert, null);
                    }
                    else if (propType == typeof(bool))
                    {
                        bool convert = Convert.ToBoolean(value);
                        info.SetValue(result, convert, null);
                    }
                    else if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(List<>))
                    {
                        List<XmlNode> xmlNodes = getNodes(node, propertyName);
                        object list = convertList(xmlNodes, propType);
                        info.SetValue(result, list, null);
                    }
                    else
                    {
                        XmlNode objectNode = getNode(node, propertyName);
                        object convert1 = convert(objectNode, info.PropertyType);
                        info.SetValue(result, convert1, null);
                    }
                }
            }
            return result;
        }

        static string MakeValue(XmlDocument doc, string propertyName)
        {
            XmlNode node = getNode(doc, propertyName);
            return node == null ? null : node.InnerText;
        }

        static string MakeValue(XmlNode xmlNode, string nodeName)
        {

            XmlNodeList nodeList = xmlNode.ChildNodes;

            foreach (XmlNode xn in nodeList)
            {
                if (nodeName.Equals(xn.Name))
                {
                    return xn.InnerText;
                }
            }
            return null;
        }

        static XmlNode getNode(XmlNode doc, string propertyName)
        {
            XmlNodeList nodeList = doc.ChildNodes;

            foreach (XmlNode xn in nodeList)
            {
                if (propertyName.Equals(xn.Name))
                {
                    return xn;
                }
            }
            return null;
        }

        static List<XmlNode> getNodes(XmlNode node, string nodeName)
        {
            List<XmlNode> result = new List<XmlNode>();
            XmlNodeList nodeList = node.ChildNodes;

            foreach (XmlNode xn in nodeList)
            {
                if (nodeName.Equals(xn.Name))
                {
                    result.Add(xn);
                }
            }
            return result;
        }
    }
}
